<script setup lang="tsx">
import TextComponent from "./models/Text.vue";
import TextareaComponent from "./models/Textarea.vue";
import PasswordComponent from "./models/Password.vue";
import NumberComponent from "./models/Number.vue";
import InputTagComponent from "./models/InputTag.vue";
import RadioComponent from "./models/Radio.vue";
import RadioGroupComponent from "./models/RadioGroup.vue";
import RadioButtonGroupComponent from "./models/RadioButtonGroup.vue";
import SelectComponent from "./models/Select.vue";
import DateTimePickerComponent from "./models/DateTimePicker.vue";
import TimePickerComponent from "./models/TimePicker.vue";
import TimeSelectComponent from "./models/TimeSelect.vue";
import ColorPickerComponent from "./models/ColorPicker.vue";

import type {
  ColorPicker,
  TimeSelect,
  TimePicker,
  DateTimePicker,
  Form,
  Text,
  Textarea,
  Password,
  Number,
  InputTag,
  Radio,
  RadioGroup,
  RadioButtonGroup,
  Select,
} from "./interface.ts";
import type { Component, ComponentInternalInstance } from "vue";
import { ref, h, getCurrentInstance, nextTick } from "vue";

const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const props = withDefaults(defineProps<Form>(), {
  prop: "",
  model: {},
  rules: undefined,
  labelWidth: 80,
  labelPosition: "left",
  labelSuffix: "",
  rowGutter: 0,
  rowJustify: "start",
  rowAlign: "top",
  rowTag: "div",
  hideRequiredAsterisk: false,
  requireAsteriskPosition: "left",
  showMessage: true,
  inlineMessage: false,
  statusIcon: false,
  validateOnRuleChange: true,
  size: "default",
  disabled: false,
  scrollToError: false,
  scrollIntoViewOptions: true,
  formItem: () => [],
  colSpans: 24
});
console.log(props)
const bidForm = ref<any>({})

const formItemList = () =>
  props.formItem.map((item) => {
    switch (true) {
      case item.type === "text":
        return ColItem(item, TextItem);
      case item.type === "textarea":
        return ColItem(item, TextareaItem);
      case item.type === "password":
        return ColItem(item, PasswordItem);
      case item.type === "number":
        return ColItem(item, NumberItem);
      case item.type === "inputtag":
        return ColItem(item, InputTagItem);
      case item.type === "radio":
        return ColItem(item, RadioItem);
      case item.type === "radiogroup":
        return ColItem(item, RadioGroup);
      case item.type === "radiobuttongroup":
        return ColItem(item, RadioButtonGroup);
      case item.type === "select":
        return ColItem(item, Select);
      case [
        "year",
        "years",
        "month",
        "months",
        "date",
        "dates",
        "datetime",
        "week",
        "datetimerange",
        "daterange",
        "monthrange",
        "yearrange",
      ].includes(item.type):
        return ColItem(item, DateTimePicker);
      case item.type === "timepicker":
        return ColItem(item, TimePicker);
      case item.type === "timeselect":
        return ColItem(item, TimeSelect);
      case item.type === "colorpicker":
        return ColItem(item, ColorPicker);
      default:
        break;
    }
  });

function ColItem(item: Form, type: Function) {
  return (
    <el-col
      span={item.colSpan || props.colSpans}
      offset={item.colOffset}
      push={item.colPush}
      pull={item.colPull}
      xs={item.colXs}
      sm={item.colSm}
      md={item.colMd}
      lg={item.colLg}
      xl={item.colXl}
      tag={item.colTag}
    >
      <el-form-item
        prop={item.prop}
        label={item.formLabel}
        label-position={item.formLabelPosition || props.labelPosition}
        label-width={item.formLabelWidth || props.labelWidth}
        required={item.formRequired}
        rules={item.formRules}
        error={item.formError}
        show-message={item.formShowMessage}
        inline-message={item.formInlineMessage}
        size={item.formSize}
        validate-status={item.formValidateStatus}
      >
        {() => (proxy!.$slots[item.prop] ? (proxy!.$slots[item.prop]?.()) : type(item, proxy!.$slots))}
      </el-form-item>
    </el-col>
  );
}

function TextItem(item: Text, slots: any): Component {
  const slotsList: { [k: string]: any } = {};
  item.suffixSlotName && (slotsList[item.suffixSlotName] = () => slots[item.suffixSlotName!]?.())
  item.prefixSlotName && (slotsList[item.prefixSlotName] = () => slots[item.prefixSlotName!]?.())
  item.prependSlotName && (slotsList[item.prependSlotName] = () => slots[item.prependSlotName!]?.())
  item.appendSlotName && (slotsList[item.appendSlotName] = () => slots[item.appendSlotName!]?.())

  return (
    <TextComponent
      type="text"
      model={item.model}
      v-model={props.model[item.model]}
      class={item.class}
      maxlength={String(item.maxlength)}
      minlength={String(item.minlength)}
      show-word-limit={item.showWordLimit}
      placeholder={item.placeholder}
      clearable={item.clearable}
      formatter={item.formatter}
      parser={item.parser}
      disabled={item.disabled}
      size={item.size}
      prefixIcon={item.prefixIcon}
      suffixIcon={item.suffixIcon}
      autocomplete={item.autocomplete}
      name={item.name}
      readonly={item.readonly}
      max={item.max}
      min={item.min}
      step={item.step}
      resize={item.resize}
      autofocus={item.autofocus}
      form={item.form}
      ariaLabel={item.ariaLabel}
      tabindex={item.tabindex}
      validate-event={item.validateEvent}
      inputStyle={item.inputStyle}
      suffixSlotName={item.suffixSlotName}
      prefixSlotName={item.prefixSlotName}
      prependSlotName={item.prependSlotName}
      appendSlotName={item.appendSlotName}
      blur={item.blur}
      focus={item.focus}
      change={item.change}
      input={item.input}
      clear={item.clear}
    >
      {slotsList}
    </TextComponent>
  ) as unknown as Component;
}

function TextareaItem(item: Textarea): Component {
  return (
    <TextareaComponent
      type="textarea"
      model={item.model}
      v-model={props.model[item.model]}
      class={item.class}
      maxlength={String(item.maxlength)}
      minlength={String(item.minlength)}
      show-word-limit={item.showWordLimit}
      placeholder={item.placeholder}
      disabled={item.disabled}
      size={item.size}
      prefix-icon={item.prefixIcon}
      suffix-icon={item.suffixIcon}
      autocomplete={item.autocomplete}
      name={item.name}
      readonly={item.readonly}
      max={item.max}
      min={item.min}
      step={item.step}
      resize={item.resize}
      autofocus={item.autofocus}
      form={item.form}
      aria-label={item.ariaLabel}
      tabindex={item.tabindex}
      validate-event={item.validateEvent}
      input-style={item.inputStyle}
      rows={item.rows}
      autosize={item.autosize}
      isComposing={item.isComposing}
      blur={item.blur}
      focus={item.focus}
      change={item.change}
      input={item.input}
      clear={item.clear}
    />
  ) as unknown as Component;
}

function PasswordItem(item: Password): Component {
  return (
    <PasswordComponent
      type="password"
      model={item.model}
      v-model={props.model[item.model]}
      maxlength={item.maxlength}
      minlength={item.minlength}
      placeholder={item.placeholder}
      clearable={item.clearable}
      disabled={item.disabled}
      size={item.size}
      prefix-icon={item.prefixIcon}
      suffix-icon={item.suffixIcon}
      autocomplete={item.autocomplete}
      name={item.name}
      readonly={item.readonly}
      max={item.max}
      min={item.min}
      step={item.step}
      resize={item.resize}
      autofocus={item.autofocus}
      form={item.form}
      aria-label={item.ariaLabel}
      tabindex={item.tabindex}
      validate-event={item.validateEvent}
      input-style={item.inputStyle}
      show-password={item.showPassword}
    />
  ) as unknown as Component;
}

function NumberItem(item: Number): Component {
  return (
    <NumberComponent
      style="width:100%;"
      type="number"
      model={item.model}
      v-model={props.model[item.model]}
      max={item.max}
      min={item.min}
      step={item.step}
      placeholder={item.placeholder}
      disabled={item.disabled}
      size={item.size}
      name={item.name}
      readonly={item.readonly}
      resize={item.resize}
      autofocus={item.autofocus}
      form={item.form}
      aria-label={item.ariaLabel}
      tabindex={item.tabindex}
      validate-event={item.validateEvent}
      step-strictly={item.stepStrictly}
      precision={item.precision}
      controls={item.controls}
      controls-position={item.controlsPosition}
      value-on-clear={item.valueOnClear}
      decreaseIcon={item.decreaseIcon}
      increaseIcon={item.increaseIcon}
      prefix={item.prefix}
      suffix={item.suffix}
    >
      {{
        "decrease-icon": () => isObject(item.decreaseIcon),
        "increase-icon": () => isObject(item.increaseIcon),
        prefix: () => isObject(item.prefix),
        suffix: () => isObject(item.suffix),
      }}
    </NumberComponent>
  ) as unknown as Component;
}

function InputTagItem(item: InputTag, slots: any): Component {
  const slotsList: { [k: string]: any } = {};
  slots[item.tagSlotName!] && slots[item.tagSlotName!]
    ? (slotsList[item.tagSlotName!] = (value: any) =>
      slots[item.tagSlotName!](value))
    : "";
  slots[item.prefixSlotName!] && slots[item.prefixSlotName!]
    ? (slotsList[item.prefixSlotName!] = (value: any) =>
      slots[item.prefixSlotName!](value))
    : "";
  slots[item.suffixSlotName!] && slots[item.suffixSlotName!]
    ? (slotsList[item.suffixSlotName!] = (value: any) =>
      slots[item.suffixSlotName!](value))
    : "";

  return (
    <InputTagComponent
      type="inputtag"
      model="inputtag"
      v-model={props.model[item.model]}
      max={item.max}
      tag-type={item.tagType}
      tag-effect={item.tagEffect}
      trigger={item.trigger}
      draggable={item.draggable}
      delimiter={item.delimiter}
      size={item.size}
      save-on-blur={item.saveOnBlur}
      clearable={item.clearable}
      disabled={item.disabled}
      validate-event={item.validateEvent}
      readonly={item.readonly}
      autofocus={item.autofocus}
      id={item.id}
      tabindex={item.tabindex}
      placeholder={item.placeholder}
      autocomplete={item.autocomplete}
      aria-label={item.ariaLabel}
      tag-slot-name={item.tagSlotName}
      prefix-slot-name={item.prefixSlotName}
      suffix-slot-name={item.suffixSlotName}
    >
      {slotsList}
    </InputTagComponent>
  );
}

function RadioItem(item: Radio, slots: any): Component {
  const slot: { [k: string]: any } = {};
  slots[item.defaultSlotName!] &&
    item.defaultSlotName &&
    (slot[item!.defaultSlotName] = (value: any) =>
      slots[item.defaultSlotName!](value));

  return (
    <RadioComponent
      type="radio"
      model="radio"
      v-model={props.model[item.model]}
      options={item.options}
      defaultSlotName={item.defaultSlotName}
    >
      {slot}
    </RadioComponent>
  ) as unknown as Component;
}

function RadioGroup(item: RadioGroup, slots: any): Component {
  const slot: { [k: string]: any } = {};
  slots[item.defaultSlotName!] &&
    item.defaultSlotName &&
    (slot[item!.defaultSlotName] = (value: any) =>
      slots[item.defaultSlotName!](value));

  return (
    <RadioGroupComponent
      type="radiogroup"
      model="radiogroup"
      v-model={props.model[item.model]}
      size={item.size}
      disabled={item.disabled}
      validate-event={item.validateEvent}
      aria-label={item.ariaLabel}
      name={item.name}
      id={item.id}
      options={item.options}
      defaultSlotName={item.defaultSlotName}
    >
      {slot}
    </RadioGroupComponent>
  );
}

function RadioButtonGroup(item: RadioButtonGroup, slots: any): Component {
  const slot: { [k: string]: any } = {};
  slots[item.defaultSlotName!] &&
    item.defaultSlotName &&
    (slot[item!.defaultSlotName] = (value: any) =>
      slots[item.defaultSlotName!](value));

  return (
    <RadioButtonGroupComponent
      type="radiobuttongroup"
      model="radiobuttongroup"
      v-model={props.model[item.model]}
      size={item.size}
      disabled={item.disabled}
      validateEvent={item.validateEvent}
      ariaLabel={item.ariaLabel}
      defaultSlotName={item.defaultSlotName}
      options={item.options}
    >
      {slot}
    </RadioButtonGroupComponent>
  );
}

function Select(item: Select, slots: any): Component {
  const slotList: { [k: string]: any } = {};
  proxy?.$slots[item.defaultSlotName!] &&
    item.defaultSlotName &&
    (slotList[item.defaultSlotName] = (value: any) =>
      slots[item.defaultSlotName!](value));
  proxy?.$slots[item.headerSlotName!] &&
    item.headerSlotName &&
    (slotList[item!.headerSlotName] = (value: any) =>
      slots[item.headerSlotName!](value));
  proxy?.$slots[item.footerSlotName!] &&
    item.footerSlotName &&
    (slotList[item!.footerSlotName] = (value: any) =>
      slots[item.footerSlotName!](value));
  proxy?.$slots[item.prefixSlotName!] &&
    item.prefixSlotName &&
    (slotList[item!.prefixSlotName] = (value: any) =>
      slots[item.prefixSlotName!](value));
  proxy?.$slots[item.emptySlotName!] &&
    item.emptySlotName &&
    (slotList[item!.emptySlotName] = (value: any) =>
      slots[item.emptySlotName!](value));
  proxy?.$slots[item.tagSlotName!] &&
    item.tagSlotName &&
    (slotList[item!.tagSlotName] = (value: any) =>
      slots[item.tagSlotName!](value));
  proxy?.$slots[item.loadingSlotName!] &&
    item.loadingSlotName &&
    (slotList[item!.loadingSlotName] = (value: any) =>
      slots[item.loadingSlotName!](value));
  proxy?.$slots[item.labelSlotName!] &&
    item.labelSlotName &&
    (slotList[item!.labelSlotName] = (value: any) =>
      slots[item.labelSlotName!](value));

  return (
    <SelectComponent
      v-model={props.model[item.model]}
      type="select"
      model="select"
      size={item.size}
      multiple={item.multiple}
      valueKey={item.valueKey}
      collapseTags={item.collapseTags}
      collapseTagsTooltip={item.collapseTagsTooltip}
      multipleLimit={item.multipleLimit}
      effect={item.effect}
      filterable={item.filterable}
      allowCreate={item.allowCreate}
      filterMethod={item.filterMethod}
      remote={item.remote}
      remoteMethod={item.remoteMethod}
      remoteShowSuffix={item.remoteShowSuffix}
      loading={item.loading}
      loadingText={item.loadingText}
      noMatchText={item.noMatchText}
      noDataText={item.noDataText}
      popperClass={item.popperClass}
      reserveKeyword={item.reserveKeyword}
      defaultFirstOption={item.defaultFirstOption}
      teleported={item.teleported}
      appendTo={item.appendTo}
      persistent={item.persistent}
      automaticDropdown={item.automaticDropdown}
      clearIcon={item.clearIcon}
      fitInputWidth={item.fitInputWidth}
      suffixIcon={item.suffixIcon}
      tagType={item.tagType}
      tagEffect={item.tagEffect}
      offset={item.offset}
      showArrow={item.showArrow}
      placement={item.placement}
      fallbackPlacements={item.fallbackPlacements}
      maxCollapseTags={item.maxCollapseTags}
      popperOptions={item.popperOptions}
      emptyValues={item.emptyValues}
      valueOnClear={item.valueOnClear}
      optionsGroup={item.optionsGroup}
      options={item.options}
      tabindex={item.tabindex}
      defaultSlotName={item.defaultSlotName}
      headerSlotName={item.headerSlotName}
      footerSlotName={item.footerSlotName}
      prefixSlotName={item.prefixSlotName}
      emptySlotName={item.emptySlotName}
      tagSlotName={item.tagSlotName}
      loadingSlotName={item.loadingSlotName}
      labelSlotName={item.labelSlotName}
    >
      {slotList}
    </SelectComponent>
  );
}

function DateTimePicker(item: DateTimePicker, slots: any) {
  const slotList: { [k: string]: any } = {};
  item.defaultSlotName && proxy?.$slots[item.defaultSlotName!] && (slotList[item.defaultSlotName] = (value: any) => slots[item.defaultSlotName!](value));
  item.rangeSeparatorSlotName && proxy?.$slots[item.rangeSeparatorSlotName!] && (slotList[item.rangeSeparatorSlotName] = (value: any) => slots[item.rangeSeparatorSlotName!](value));
  item.prevMonthSlotName && proxy?.$slots[item.prevMonthSlotName!] && (slotList[item.prevMonthSlotName] = (value: any) => slots[item.prevMonthSlotName!](value));
  item.nextMonthSlotName && proxy?.$slots[item.nextMonthSlotName!] && (slotList[item.nextMonthSlotName] = (value: any) => slots[item.nextMonthSlotName!](value));
  item.prevYearSlotName && proxy?.$slots[item.prevYearSlotName!] && (slotList[item.prevYearSlotName] = (value: any) => slots[item.prevYearSlotName!](value));
  item.nextYearSlotName && proxy?.$slots[item.nextYearSlotName!] && (slotList[item.nextYearSlotName] = (value: any) => slots[item.nextYearSlotName!](value));

  return (
    <DateTimePickerComponent
      type={item.type}
      model={item.model}
      v-model={props.model[item.model]}
      readonly={item.readonly}
      disabled={item.disabled}
      editable={item.editable}
      clearable={item.clearable}
      size={item.size}
      placeholder={item.placeholder}
      startPlaceholder={item.startPlaceholder}
      endPlaceholder={item.endPlaceholder}
      arrowControl={item.arrowControl}
      format={item.format}
      popperClass={item.popperClass}
      rangeSeparator={item.rangeSeparator}
      defaultValue={item.defaultValue}
      defaultTime={item.defaultTime}
      valueFormat={item.valueFormat}
      dateFormat={item.dateFormat}
      timeFormat={item.timeFormat}
      unlinkPanels={item.unlinkPanels}
      prefixIcon={item.prefixIcon}
      clearIcon={item.clearIcon}
      shortcuts={item.shortcuts}
      disabledDate={item.disabledDate}
      cellClassName={item.cellClassName}
      teleported={item.teleported}
      emptyValues={item.emptyValues}
      valueOnClear={item.valueOnClear}
      showNow={item.showNow}
      defaultSlotName={item.defaultSlotName}
      rangeSeparatorSlotName={item.rangeSeparatorSlotName}
      prevMonthSlotName={item.prevMonthSlotName}
      nextMonthSlotName={item.nextMonthSlotName}
      prevYearSlotName={item.prevYearSlotName}
      nextYearSlotName={item.nextYearSlotName}
    >
      {slotList}
    </DateTimePickerComponent>
  );
}

function TimePicker(item: TimePicker) {
  return (
    <TimePickerComponent
      type={item.type}
      model={item.model}
      v-model={props.model[item.model]}
      readonly={item.readonly}
      disabled={item.disabled}
      editable={item.editable}
      clearable={item.clearable}
      size={item.size}
      placeholder={item.placeholder}
      start-placeholder={item.startPlaceholder}
      end-placeholder={item.endPlaceholder}
      is-range={item.isRange}
      arrow-control={item.arrowControl}
      popper-class={item.popperClass}
      range-separator={item.rangeSeparator}
      format={item.format}
      default-value={item.defaultValue}
      value-format={item.valueFormat}
      id={item.id}
      name={item.name}
      aria-label={item.ariaLabel}
      prefix-icon={item.prefixIcon}
      clear-icon={item.clearIcon}
      disabled-hours={item.disabledHours}
      disabled-minutes={item.disabledMinutes}
      disabled-seconds={item.disabledSeconds}
      teleported={item.teleported}
      tabindex={item.tabindex}
      empty-values={item.emptyValues}
      value-on-clear={item.valueOnClear}
    />
  );
}

function TimeSelect(item: TimeSelect) {
  return (
    <TimeSelectComponent
      type="timeselect"
      model={item.model}
      v-model={props.model[item.model]}
      readonly={item.readonly}
      disabled={item.disabled}
      editable={item.editable}
      clearable={item.clearable}
      include-end-time={item.includeEndTime}
      size={item.size}
      placeholder={item.placeholder}
      effect={item.effect}
      prefix-icon={item.prefixIcon}
      clear-icon={item.clearIcon}
      start={item.start}
      end={item.end}
      step={item.step}
      min-time={item.minTime}
      max-time={item.maxTime}
      format={item.format}
      empty-values={item.emptyValues}
    />
  );
}

function ColorPicker(item: ColorPicker) {
  return (
    <ColorPickerComponent
      type={item.type}
      model={item.model}
      v-model={props.model[item.model]}
      disabled={item.disabled}
      size={item.size}
      show-alpha={item.showAlpha}
      color-format={item.colorFormat}
      popper-class={item.popperClass}
      predefine={item.predefine}
      validate-event={item.validateEvent}
      tabindex={item.tabindex}
      aria-label={item.ariaLabel}
      teleported={item.teleported}
      change={item.change}
      active-change={item.activeChange}
      focus={item.focus}
      blur={item.blur}
    />
  );
}

const render = () => (
  <el-form
    ref={bidForm}
    model={props.model}
    rules={props.rules}
    inline={props.inline}
    label-position={props.labelPosition}
    label-width={props.labelWidth}
    label-suffix={props.labelSuffix}
    hide-required-asterisk={props.hideRequiredAsterisk}
    require-asterisk-position={props.requireAsteriskPosition}
    show-message={props.showMessage}
    inline-message={props.inlineMessage}
    status-icon={props.statusIcon}
    validate-on-rule-change={props.validateOnRuleChange}
    size={props.size}
    disabled={props.disabled}
    scroll-to-error={props.scrollToError}
    scroll-into-view-options={props.scrollIntoViewOptions}
  >
    <el-row
      gutter={props.rowGutter}
      justify={props.rowJustify}
      align={props.rowAlign}
      tag={props.rowTag}
    >
      {formItemList()}
    </el-row>
  </el-form>
);

function isObject(slot: any) {
  if (typeof slot === "object") {
    return h(slot);
  } else {
    return slot;
  }
}

function validate(callback = () => { }) {
  return bidForm.value?.validate(callback)
}

function validateField(attri: any, callback = () => { }) {
  return bidForm.value?.validateField(attri, callback)
}

function resetFields() {
  return bidForm.value?.resetFields()
}

function scrollToField(attri: string) {
  return bidForm.value?.scrollToField(attri)
}

function clearValidate(attri: any) {
  return bidForm.value?.clearValidate(attri)
}

function getField(attri: string) {
  return bidForm.value?.getField(attri)
}

const fields = ref([])
nextTick(() => {
  fields.value = bidForm.value?.fields
})


defineExpose({
  validate,
  validateField,
  resetFields,
  scrollToField,
  clearValidate,
  fields,
  getField
})
</script>

<template>
  <render />
</template>
